<template>
  <div class="ckerk_time_line__">
    <!--时间线-->
    <div class="time_line">
      <div class="time_line_history">
        <div class="tl-timeline">
          <div v-for="item in recordList" :key="item.id" class="tl-container right">
            <div class="date">{{ item.id }}</div>
            <div class="content">
              <h2>
                {{ item.createTime }}
                <span :style="{ color: getRecordColor(item.type), margin: '0 0 0 5px' }">
                  {{ item.type | recordTypeFlt }}
                </span>
                <span
                  v-if="item.type === RecordType.CONSULTATION.id"
                  :style="{ color: item.status === 0 ? '#666' : 'green', margin: '0 0 0 5px' }">
                  {{ item.status === 0 ? '未成交' : '成交' }} {{ item.status !== 0 ? ' ' + item.money : '' }}
                </span>
                <span
                  v-if="item.type === RecordType.APPLY_ORDER.id"
                  :style="{
                    color: item.status === 0 ? '#663399' : item.status === 1 ? '#009999' : '#993399',
                    margin: '0 0 0 5px',
                  }">
                  {{ item.status === 0 ? '开单' : item.status === 1 ? '跟进' : '关闭' }}
                </span>
              </h2>
              <div class="time_line_body">
                <div v-if="item.type === RecordType.IMAGE.id">
                  <el-link
                    v-if="item.content && item.content.length"
                    :href="getClerkImgUri(item.url)"
                    target="_blank"
                    type="success"
                    style="margin-right: 5px">
                    {{ item.content }}
                  </el-link>
                  <el-button v-else type="success" size="mini" plain @click.stop="goShowClerkImage(item)">
                    图片
                  </el-button>
                </div>
                <div
                  v-else-if="
                    item.type === RecordType.AUDIO.id || (item.type === RecordType.APPLY_ORDER.id && item.url)
                  ">
                  <audio :src="getClerkImgUri(item.url)" controls style="width: 80%"></audio>
                </div>
                <div v-else-if="item.type === RecordType.FACIAL.id">
                  <div class="facial__" @click.stop="goShowClerkFacial(item)">
                    {{ item.content }}
                    <div v-if="item.status === 1" class="facial_good">{{ item.item }}</div>
                    <div v-if="item.status === 2" class="facial_bad">{{ item.item }}</div>
                  </div>
                </div>
                <div v-else-if="item.type === RecordType.ACTIVITY.id" class="consultation">
                  <div v-if="item.scene">类型: {{ item.scene }}</div>
                  <div v-if="item.trust">时间: {{ item.trust }}</div>
                  <div v-if="item.status !== undefined">参与: {{ item.status === 0 ? '未出席' : '已出席' }}</div>
                  <div v-if="item.content && item.content.length">反馈: {{ item.content }}</div>
                </div>
                <div v-else>
                  <span>{{ item.content }}</span>
                  <div v-if="item.type === RecordType.CONSULTATION.id" class="consultation">
                    <div v-if="item.scene">场景: {{ item.scene }}</div>
                    <div v-if="item.trust">信任: {{ item.trust }}</div>
                    <div v-if="item.item">品项: {{ item.item }}</div>
                  </div>
                </div>
                <div class="recorder__">{{ item.recorder }}</div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="time_line_new">
        <el-radio-group v-model="replyType" size="mini">
          <el-radio :label="1">复盘</el-radio>
          <el-radio :label="2">会诊</el-radio>
        </el-radio-group>
        <el-switch
          v-if="isCoach && replyType == 2"
          v-model="mic.isRecOn"
          active-text="音频"
          inactive-text="文字"
          :disabled="!(rid > 0)"
          style="margin: 0 0 0 20px"
          @change="handleOpenRecord"></el-switch>
        <div v-if="replyType == 1">
          <div style="margin: 10px; text-align: center">
            <el-input
              v-model="newReplay.content"
              size="small"
              style="width: 100%"
              type="textarea"
              maxlength="500"
              show-word-limit
              :autosize="{ minRows: 4, maxRows: 4 }"
              placeholder="请输入复盘内容"></el-input>
            <el-button type="primary" size="small" style="width: 100px; margin-top: 10px" @click="addReplay">
              提交
            </el-button>
          </div>
          <div>
            <el-upload
              style="text-align: center"
              action="/"
              :show-file-list="false"
              :limit="1"
              :before-upload="beforeUpload"
              :http-request="onUploadFile"
              accept=".png,.jpg,.jpeg,.mp3,.m4a,.amr,.acc,.wav">
              <el-button type="primary" size="small" plain>上传图片或音频</el-button>
            </el-upload>
          </div>
        </div>
        <div v-if="replyType == 2" style="margin: 10px; text-align: center">
          <div v-if="mic.isRecOn">
            <el-button type="primary" size="mini" plain :disabled="mic.isRecord" @click.stop="startRecorder">
              开始
            </el-button>
            <el-button
              type="warning"
              size="mini"
              plain
              :disabled="!(mic.initDone && mic.isRecord && !mic.audioReady)"
              @click.stop="stopRecorder">
              停止
            </el-button>
            <el-button
              type="info"
              size="mini"
              plain
              :disabled="!(mic.initDone && mic.isRecord && !mic.isPaused)"
              @click.stop="pauseRecorder">
              暂停
            </el-button>
            <el-button
              type="success"
              size="mini"
              plain
              :disabled="!(mic.initDone && mic.isRecord && mic.isPaused)"
              @click.stop="resumeRecorder">
              恢复
            </el-button>
            <br />
            <audio v-if="mic.audioReady" ref="refAudioPlayer" :src="mic.audioUrl" controls></audio>
            <div v-else style="border: 1px solid #ccc; display: inline-block; vertical-align: bottom">
              <div ref="refRecWave" style="height: 100px; width: 300px" class="recwave"></div>
            </div>
          </div>
          <el-input
            v-else
            v-model="newReplay.content"
            size="small"
            style="width: 100%"
            type="textarea"
            maxlength="500"
            show-word-limit
            :autosize="{ minRows: 4, maxRows: 4 }"
            :disabled="isCoach && !(rid > 0)"
            :placeholder="rid > 0 ? '请输入回复内容' : '请描述会诊需求'"></el-input>
          <el-button
            type="primary"
            size="small"
            :disabled="isCoach && !(rid > 0)"
            style="width: 100px; margin-top: 10px"
            @click="goReplyOrder">
            提交
          </el-button>
          <el-button
            v-if="rid > 0"
            type="info"
            size="small"
            style="width: 100px; margin-top: 10px"
            @click="goCloseOrder">
            关闭会诊
          </el-button>
        </div>
      </div>
    </div>
    <!--人脸模型-->
    <div v-if="showFacialPage || showImagePage" class="facial_model">
      <clerk-facial v-if="showFacialPage" ref="refClerkFacial"></clerk-facial>
      <img v-if="showImagePage" :src.sync="imageUrl" width="100%" />
      <div class="facial_model_close" @click.stop="goHideClerkFacial">
        <i class="el-icon-circle-close"></i>
      </div>
    </div>
  </div>
</template>

<script>
import * as apiSts from '@/api/_com_';
// 引入mp3格式支持文件
import Recorder from 'recorder-core';
import 'recorder-core/src/engine/mp3';
import 'recorder-core/src/engine/mp3-engine';
import 'recorder-core/src/extensions/lib.fft';
import 'recorder-core/src/extensions/wavesurfer.view';
// 引入后端 API
import { closeOrderRecord, replyOrderRecord, replyOrderAudio } from '@/api/record';
import { uploadFile } from '@/api/txs';
import { addRecord, getRecordsByCid } from '@/api/ym';
import '@/assets/css/timeline2.css';
// 引入自定义组件
import WebConst from '@/utils/const/web-const';
import RecordType from '@/utils/enum/record-type';
import { isCoachRole } from '@/utils/enum/roles';
import ClerkFacial from './Facial.vue';
// 引入第三方组件
import { MessageBox } from 'element-ui';
import { mapGetters } from 'vuex';

export default {
  name: 'ClerkTimeLine',
  components: { 'clerk-facial': ClerkFacial },
  filters: {
    recordTypeFlt(type) {
      const tmp = RecordType.getById(type);
      if (!tmp) {
        return '未知';
      }
      return tmp.name;
    },
  },
  data() {
    return {
      rid: undefined,
      replyType: 1,
      isFetching: false,
      RecordType: RecordType,
      showImagePage: false,
      showFacialPage: false,
      imageUrl: '',
      recordList: [],
      newReplay: {
        content: '',
        cid: null,
        recorder: null,
        type: 2,
      },
      mic: {
        isRecOn: false,
        initDone: false,
        isRecord: false,
        isPaused: false,
        recorder: null,
        waveview: null,
        audioUrl: '',
        audioBlob: null,
        audioReady: false,
      },
    };
  },
  computed: {
    ...mapGetters(['name', 'id', 'deptId', 'openId']),
    isCoach() {
      return isCoachRole(this.deptId);
    },
  },
  methods: {
    getRecordColor(type) {
      switch (type) {
        case RecordType.CONSULTATION.id:
        case RecordType.FACIAL.id:
          return '#FF7510';
        case RecordType.FOLLOW_UP.id:
          return '#EFAC00';
        case RecordType.EMPOWERMENT.id:
          return '#00A61B';
        case RecordType.CUSTOMER_RELATIONSHIP.id:
          return '#1073FF';
        case RecordType.APPLY_ORDER.id:
          return 'red';
      }
    },
    getClerkImgUri(filename) {
      return WebConst.getClerkImageUriByName(filename);
    },
    loadClerkRecords: async function (cid, rid) {
      if (this.isFetching) {
        return;
      }
      this.isFetching = true;
      this.rid = rid;
      this.newReplay.cid = cid;
      this.newReplay.recorder = this.name;
      const errAlertMsg = '获取顾客时间线失败';
      await getRecordsByCid(cid)
        .then(res => {
          if (res.data.status !== apiSts.ResponseOK) {
            if (res.data.message) {
              MessageBox.alert(res.data.message);
            } else {
              MessageBox.alert(errAlertMsg);
            }
            return;
          }
          this.recordList.splice(0, this.recordList.length);
          if (res.data.data && res.data.data.length > 0) {
            this.recordList.push(...res.data.data);
          }
        })
        .catch(err => {
          console.log('getRecordsByCid', err);
          MessageBox.alert(errAlertMsg);
        })
        .finally(() => {
          this.isFetching = false;
        });
    },
    addReplay: async function (upType) {
      if (upType) {
        this.newReplay.type = upType;
      } else {
        this.newReplay.type = RecordType.REPLAY.id;
      }
      this.newReplay.status = 1;
      const errAlertMsg = '添加失败';
      addRecord(this.newReplay)
        .then(res => {
          if (res.data.status !== apiSts.ResponseOK) {
            if (res.data.message) {
              MessageBox.alert(res.data.message);
            } else {
              MessageBox.alert(errAlertMsg);
            }
            return;
          }
          this.newReplay.content = '';
          this.loadClerkRecords(this.newReplay.cid, this.rid);
        })
        .catch(err => {
          console.error(err);
          MessageBox.alert(errAlertMsg);
        });
    },
    goCloseOrder: async function () {
      const params = {};
      params.cid = this.newReplay.cid;
      params.rid = this.rid > 0 ? this.rid : null;
      const errAlertMsg = '关闭会诊失败';
      await closeOrderRecord(params)
        .then(res => {
          if (res.data.status !== apiSts.ResponseOK) {
            if (res.data.message) {
              MessageBox.alert(res.data.message);
            } else {
              MessageBox.alert(errAlertMsg);
            }
            return;
          }
          this.loadClerkRecords(this.newReplay.cid, -1);
        })
        .catch(err => {
          console.error(err);
          MessageBox.alert(errAlertMsg);
        });
    },
    goReplyOrder: async function () {
      if (this.rid > 0) {
        if (this.mic.isRecOn) {
          await this.replyAudio();
        } else {
          await this.replyOrder();
        }
      } else {
        await this.openOrder();
      }
    },
    openOrder: async function () {
      this.newReplay.type = RecordType.APPLY_ORDER.id;
      this.newReplay.status = 0;
      const errAlertMsg = '添加会诊失败';
      addRecord(this.newReplay)
        .then(res => {
          if (res.data.status !== apiSts.ResponseOK) {
            if (res.data.message) {
              MessageBox.alert(res.data.message);
            } else {
              MessageBox.alert(errAlertMsg);
            }
            return;
          }
          this.newReplay.content = '';
          const ret = res.data.data;
          if (this.rid > 0) {
            this.loadClerkRecords(ret.cid, this.rid);
          } else {
            this.loadClerkRecords(ret.cid, ret.id);
          }
        })
        .catch(err => {
          console.error(err);
        });
    },
    replyOrder: async function () {
      if (this.newReplay.content) {
        this.newReplay.content = this.newReplay.content.trim();
      }
      if (!this.newReplay.content) {
        MessageBox.alert('会诊回复内容不能为空');
        return;
      }
      const params = {};
      params.cid = this.newReplay.cid;
      params.rid = this.rid > 0 ? this.rid : null;
      params.reply = this.newReplay.content;
      const errAlertMsg = '回复会诊失败';
      await replyOrderRecord(params)
        .then(res => {
          if (res.data.status !== apiSts.ResponseOK) {
            if (res.data.message) {
              MessageBox.alert(res.data.message);
            } else {
              MessageBox.alert(errAlertMsg);
            }
            return;
          }
          this.newReplay.content = '';
          this.loadClerkRecords(this.newReplay.cid, this.rid);
        })
        .catch(err => {
          console.error(err);
          MessageBox.alert(errAlertMsg);
        });
    },
    replyAudio: async function () {
      if (!this.mic.audioReady) {
        MessageBox.alert('会诊回复语音不能为空');
        return;
      }
      const dataInfo = new FormData();
      dataInfo.append('file', this.mic.audioBlob, Math.floor(Math.random() * 1000) + '.mp3');
      dataInfo.append('cid', this.newReplay.cid);
      dataInfo.append('rid', this.rid > 0 ? this.rid : null);
      const errAlertMsg = '回复会诊失败';
      await replyOrderAudio(dataInfo)
        .then(res => {
          if (res.data.status !== apiSts.ResponseOK) {
            if (res.data.message) {
              MessageBox.alert(res.data.message);
            } else {
              MessageBox.alert(errAlertMsg);
            }
            return;
          }
          this.mic.audioBlob = null;
          this.loadClerkRecords(this.newReplay.cid, this.rid);
        })
        .catch(err => {
          console.error(err);
          MessageBox.alert(errAlertMsg);
        });
    },
    beforeUpload: async function (file) {},
    onUploadFile: async function (options) {
      const file = options.file;
      let upType = undefined;
      if (file.type.includes('image/')) {
        upType = RecordType.IMAGE.id;
      } else if (file.type.includes('audio/')) {
        upType = RecordType.AUDIO.id;
      } else {
        this.$alert('请上传图片或音频！', '错误', {
          type: 'error',
          confirmButtonText: '确定',
        });
        upType = RecordType.CONSULTATION.id;
        return;
      }
      const dataInfo = new FormData();
      dataInfo.append('file', file);
      uploadFile(dataInfo).then(res => {
        this.fileUrl = res.data.data;
        this.newReplay.status = 0;
        this.newReplay.type = upType;
        this.newReplay.url = this.fileUrl;
        this.addReplay(upType);
      });
    },
    goShowClerkImage: async function (val) {
      if (!val.url) {
        return;
      }
      this.imageUrl = this.getClerkImgUri(val.url);
      this.showImagePage = true;
      this.showFacialPage = false;
    },
    goShowClerkFacial: async function (val) {
      this.showImagePage = false;
      this.showFacialPage = true;
      await this.$nextTick();
      this.$refs.refClerkFacial.showFacialModel(val.trust);
    },
    goHideClerkFacial: async function () {
      if (this.$refs.refClerkFacial) {
        this.$refs.refClerkFacial.showFacialModel([]);
      }
      this.showImagePage = false;
      this.showFacialPage = false;
    },
    handleOpenRecord: async function () {
      if (this.mic.isRecOn) {
        // this.initRecorder();
      }
    },
    initRecorder() {
      this.mic.recorder = Recorder({
        type: 'mp3',
        sampleRate: 16000,
        bitRate: 16,
        onProcess: (buffers, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) => {
          if (this.mic.waveview) {
            this.mic.waveview.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
          }
        },
      });
    },
    startRecorder: async function () {
      if (this.mic.isRecord) {
        return;
      }
      this.mic.audioUrl = '';
      this.mic.audioBlob = null;
      this.mic.audioReady = false;
      const that = this.mic;

      this.initRecorder();
      this.mic.recorder.open(
        function () {
          // 打开麦克风授权获得相关资源
          that.initDone = true;
          that.waveview = Recorder.WaveSurferView({ elem: '.recwave', width: 300, height: 100 });
          that.recorder.start();
          that.isRecord = true;
        },
        function (msg, isUserNotAllow) {
          that.initDone = false;
          that.isRecord = false;
          if (isUserNotAllow) {
            MessageBox.alert('录音必须允许浏览器使用麦克风');
            return;
          }
          // 不支持
          MessageBox.alert('无法录音: ' + msg);
        }
      );
    },
    stopRecorder: async function () {
      if (!this.mic.isRecord) {
        return;
      }
      await this.mic.recorder.stop(
        (blob, duration) => {
          this.mic.audioUrl = URL.createObjectURL(blob);
          this.mic.audioBlob = blob;
          this.mic.audioReady = true;
          this.mic.recorder.close();
          this.mic.recorder = null;
          this.mic.waveview = null;
          this.mic.isRecord = false;
          console.log('时长: ' + duration + ' ms');
        },
        err => {
          console.error('录音出错：' + err);
          this.mic.recorder.close(); // 关闭录音，释放录音资源
          this.mic.recorder = null;
          this.mic.waveview = null;
          this.mic.isRecord = false;
        }
      );
      if (this.$refs.refRecWave) {
        // 删除 refRecWave 的所有子 DOM
        while (this.$refs.refRecWave.firstChild) {
          this.$refs.refRecWave.removeChild(this.$refs.refRecWave.firstChild);
        }
      }
    },
    pauseRecorder: async function () {
      this.mic.isPaused = true;
      await this.mic.recorder.pause();
    },
    resumeRecorder: async function () {
      this.mic.isPaused = false;
      await this.mic.recorder.resume();
    },
  },
};
</script>

<style lang="scss" scoped>
@import '@/assets/css/scrollbar.scss';

.ckerk_time_line__ {
  width: 100%;
  padding: 0 0 0 0;
  display: flex;
  flex-direction: row-reverse;
  justify-content: flex-start;
  align-items: normal;
  background: none;

  .time_line {
    width: 100%;
    height: 100vh;
    background-color: beige;
    opacity: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: flex-start;

    ul {
      padding-inline-start: 10px;
    }

    .time_line_history {
      width: 100%;
      height: 70vh;
      padding: 0 10px 0 0;
      display: block;
      overflow-x: hidden;
      overflow-y: scroll;
      background: linear-gradient(to bottom, #e5e8ed, #efeff1);
      border: 1px solid #e5e8ed;
      border-radius: 5px 5px 0 0;

      .time_line_body {
        min-height: 40px;
        margin: 0;
        padding: 0 5px 0 5px;
        position: relative;
        font-size: 16px;
        line-height: 16px;
        color: #454545;

        .facial__ {
          cursor: pointer;
          position: relative;
          padding: 20px 0 20px 0;

          .facial_good {
            position: absolute;
            left: 0px;
            top: 0px;
            margin: 0 0 5px 0;
            padding: 1px 4px 0 4px;
            background-color: #e5f0ff;
            border-radius: 6px;
            font-size: 10px;
            color: #237eff;
          }

          .facial_bad {
            position: absolute;
            left: 0px;
            top: 0px;
            margin: 0 0 5px 0;
            padding: 1px 4px 0 4px;
            background-color: #fee9e9;
            border-radius: 6px;
            font-size: 10px;
            color: #ff4d4d;
          }
        }

        .consultation {
          margin: 5px 0 0 0;
          font-size: 13px;
          display: flex;
          flex-direction: column;
          align-items: flex-start;
          justify-content: flex-start;
          gap: 2px;
        }

        .recorder__ {
          position: absolute;
          right: 10px;
          bottom: -5px;
          color: #666666;
          font-size: 10px;
        }
      }
    }

    .time_line_new {
      width: 100%;
      height: 30vh;
      padding: 0 0 5px 0;
      display: block;
      background: linear-gradient(to bottom, #efeff1, #e5e8ed);
      border: 1px solid #e5e8ed;
      border-radius: 0 0 5px 5px;
    }
  }

  .facial_model {
    width: 40vw;
    height: 100vh;
    position: fixed;
    top: 0;
    right: 30vw;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    background-color: #ffffff;

    .facial_model_close {
      position: absolute;
      top: 20px;
      right: 20px;
      cursor: pointer;
    }
  }
}
</style>
